# DictTensor.py
# 这个类功能清晰，所有的计算内容一目了然，不需要重构。
import numpy as np
from constants import WXDiff,ZHI_TO_WX  # 原：zhiToWxIdxLst
from core.StemBranchCalculator import StemBranchCalculator  # 原CoinNumVector，恰好对应getMask,toZhi,toGan三函数
from mapping.idx2numLst import _get_num_lst  # 原CoinNumVector，替代fromGuaIdx(idx).numLst
from mapping.GuaNameMapper import GuaNameMapper
from utils.for_np import apply_mapping  # 原：function_tools.dataset.func
from utils.attr_dict import AttrFuncDict

class DictTensor:
    '''提供基础核心的计算功能，debug开不开都一样'''
    def __init__(self, gua_dict, debug=False):
        if isinstance(gua_dict,AttrFuncDict):
            pass
        elif isinstance(gua_dict,dict):
            gua_dict = AttrFuncDict(**gua_dict)
        else:
            raise TypeError(f'gua_dict must be AttrFuncDict or dict,{type(gua_dict)}')
        self.gua = gua_dict
        self.debug = debug
        self.sbc = StemBranchCalculator(self.gua['coinNumLst'], have_hide=True)
        self.have_hide = True
        self.apply_mask = False
        self._yinyang = None  # 下述全部(3,6)矩阵，共3rows，row0为主卦，row1为变卦，row2为伏藏本宫六纯卦
        self._gan = None
        self._zhi = None
        self._wx = None
        self._block = None
        self._mask = None
        self._wxself = None
        self._zhiDiffSelf = None
        self._zhiSumSelf = None


    @property
    def yinyang(self):
        if self._yinyang is None:
            gua = self.gua['gua']
            biangua = self.gua['biangua']
            home_gua = self.gua['gua'] // 8 * 8
            if self.debug: print(
                '主卦：', GuaNameMapper.by_idx(gua), '变卦：',
                GuaNameMapper.by_idx(biangua) if gua != biangua else '无变卦')
            yinyang = np.array(list(map(_get_num_lst, [gua, biangua, home_gua])))
            yinyang[~self.mask] = -1
            self._yinyang = yinyang
        return self._yinyang

    @property
    def mask(self):
        if self._mask is None:
            self._mask = np.array(self.sbc.get_mask())
            if self.debug: print('检查 self._mask 是否达到预期？', self._mask)
        return self._mask

    @property
    def zhi(self):
        if self._zhi is None:
            self._zhi = np.array(self.sbc.to_zhi(self.apply_mask), dtype=np.int8)
            self._zhi[~self.mask] = -1
        return self._zhi

    @property
    def gan(self):
        if self._gan is None:
            self._gan = np.array(self.sbc.to_gan(self.apply_mask), dtype=np.int8)
            self._gan[~self.mask] = -1
        return self._gan

    @property
    def wx(self):
        if self._wx is None:
            self._wx = apply_mapping(self.zhi, ZHI_TO_WX)
            # self._wx[~self.mask] = -1
        return self._wx

    @property
    def block(self):
        '''把卦象分块，主卦安静标记为0，主卦发动标记为1，变卦标记为2，伏藏标记为3'''
        if self._block is None:
            tgl = self.gua['triggerLst']
            # 提取 row2 和 row3
            row2 = self.mask[1]  # 第二行
            row3 = self.mask[2]  # 第三行

            # 对 row2 进行映射：True -> 2, False -> -1
            row2_mapped = np.where(row2, 2, -1)

            # 对 row3 进行映射：True -> 3, False -> -1
            row3_mapped = np.where(row3, 3, -1)

            if self.debug: print("Row 2 (分块结果):", row2_mapped, "\nRow 3 (分块结果):", row3_mapped)
            self._block = np.array([tgl, row2_mapped, row3_mapped], dtype=np.int8)
        return self._block

    @property
    def vary(self):
        '''选中动爻及其变爻 -- 动

        - 与对应的还还有拓展问题：妻财化出？？
        原本：选中某个平面 plane，月克，月破，直接对应wx，zhi层就能选中

        现在：化月破，需要把wx or zhi层移到和plane对齐的位置wx_1,zhi_1，支持&|运算了。
        '''
        tgl = self.gua['triggerLst']
        return np.array([tgl, tgl, [0] * len(tgl)], dtype=np.uint8)

    def wx_alter(self):
        '''*变爻对本位动爻的生克*

        - **词汇**：对应 `回头扶/回头生/回头克` 等
        - **简介**：找出所有被【回头生/回头克】的动爻，【飞神生/克】的伏神
        - **解释**: 0-扶，1-生，2-克(主卦位置被回头克/变爻位置对本位爻回头克/伏神位置被飞神克)， 3-耗，4-泄。即与WXDiff一致。
        '''
        if self._wxself is None:  # 只考虑了12对生克关系，比较简单。
            arr = self.wx.copy()
            # print('arr',arr)
            arr[2] = arr[2] - arr[0] + 5
            arr[0] = arr[0] - arr[1] + 5 # 主卦 - 变卦
            arr = arr % 5
            arr[1, :] = arr[0, :]
            # arr[1,:]=-1
            arr[~self.mask] = -1    # 过滤无效区域
            mask = np.array(self.gua['triggerLst'], dtype=bool)
            arr[0][~mask] = -1  # 过滤非动爻区域
            self._wxself = arr
        return self._wxself

    def wx_vary(self, compare=2):
        '''*动爻对于所有爻的生克(五行差值)*

        - **词汇**： 对应 `动爻生/动爻克` 等
        - **意义**： 找出所有主卦上所有被【动爻生/克】的位置。不重复遍历每个动爻的wx_value，挑选出满足条件的位置
        - **复杂**： 列出所有需要考虑了6*5对生克关系，有多少个动爻，你就需要给罗出来多少层。
        '''
        # print('compare',compare,f'代表着动爻{WXDiff[compare]}')

        result = np.zeros((3, 6), dtype=np.uint8)

        given_list = self.gua['triggerLst']

        # 遍历给定的列表
        hash_map = set()
        for k in range(6):
            if given_list[k] == 1:
                target_value = self.wx[0, k]  # 动爻的五行（金=0，水=1）
                if target_value in hash_map:
                    continue
                hash_map.add(target_value)

                # 遍历整个wx数组，寻找比threshold刚好大2的元素（介于0-4之间是有效值，用取模的逻辑：3-1=2，2-0=2，1-4=2，0-3=2）
                mask = self.wx - (target_value + compare) % 5 == 0
                # mask = ((wx - A) % 5 == compare) & (wx >= 0)
                result = np.logical_or(result, mask)
        return result

    def zhi_alter(self, mode='zhiDiff'):
        '''*变爻对本位动爻的地支差值*

        - **公式**：`alter_zhi` -/+ `vary_zhi`
        - **意义**：
            - (zhiDiff)0-伏吟，1-化进，6-反吟，11-化退
            - (zhiSum)3-化和,9-化害'''
        arr = self.zhi.copy()
        # 避免变卦arr[1]的无效值-1干扰和差
        triggerLstMask = np.array(self.gua['triggerLst'], dtype=bool)
        if mode == 'zhiDiff':
            if self._zhiDiffSelf is None:  # 只考虑了6对关系，比较简单。
                arr[0] = arr[1] - arr[0] + 12
                arr = arr % 12
                arr[0, :][~triggerLstMask] = -1
                arr[1, :] = arr[0, :]
                # arr[1,:]=-1
                arr[2, :] = -1
                arr[~self.mask] = -1
                self._zhiDiffSelf = arr
            return self._zhiDiffSelf
        elif mode == 'zhiSum':
            if self._zhiSumSelf is None:  # 只考虑了12对生克关系，比较简单。
                arr[0] = arr[1] + arr[0]  # bug:arr[0]=[1,2,3,4,5,6],arr[1]=[-1,2,2,3,4,-1] ,-1是无效值，不希望相加。
                arr = arr % 12
                arr[0, :][~triggerLstMask] = -1
                arr[1, :] = arr[0, :]
                # arr[1,:]=-1
                arr[2, :] = -1
                arr[~self.mask] = -1
                self._zhiSumSelf = arr
            return self._zhiSumSelf
        else:
            raise ValueError(f'不期望的mode: {mode}')

    def zhi_vary(self, mode='zhiDiff', compare=6):
        """
        遍历所有动爻，计算主卦中每个爻与动爻之间的地支差值(zhiDiff)或和值(zhiSum)，
        并判断是否等于指定的 compare 值。

        参数:
            mode: 'zhiDiff' 表示计算差值；'zhiSum' 表示计算和值
            compare: 指定要匹配的地支关系值，如 6(冲)、3(合)、0(相同)

        返回:
            result: 布尔型数组 (3, 6)，标记满足条件的位置
        """
        result = np.zeros((3, 6), dtype=bool)
        given_list = self.gua['triggerLst']
        zhi_values = self.zhi[0]  # 主卦的地支值列表

        hash_map = set()  # 防止重复处理相同的地支值
        for k in range(6):
            if given_list[k] == 1:
                target_zhi = zhi_values[k]
                if target_zhi in hash_map:
                    continue
                hash_map.add(target_zhi)

                if mode == 'zhiDiff':
                    diff = (zhi_values - target_zhi + 12) % 12
                    mask = (diff == compare)
                elif mode == 'zhiSum':
                    total = (zhi_values + target_zhi) % 12
                    mask = (total == compare)
                else:
                    raise ValueError(f"未知模式: {mode}")

                # 只在有效位置上设置 True
                result[0, mask] = True
                # result[1, mask] = True  # 可选：扩展到变卦层
                # result[2, mask] = True  # 可选：扩展到伏藏层
        # print('mode:',mode,'\ncompare:',compare,'\nresult:',result)
        return result

